package org.dromara.system.kernel;

import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.BeanUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.PayOrder;
import org.dromara.system.domain.bo.chart.SettlementAllBo;
import org.dromara.system.domain.bo.chart.SettlementListBo;
import org.dromara.system.domain.bo.chart.SettlementMonthBo;
import org.dromara.system.domain.bo.chart.SettlementUnBo;
import org.dromara.system.domain.vo.PayOrderVo;
import org.dromara.system.elastic.entity.PayOrderEs;
import org.dromara.system.mapper.PayOrderMapper;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.ScriptQueryBuilder;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.Sum;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@RefreshScope
@Service
@RequiredArgsConstructor
public class OrderSettKernel {

    private final OrderSearchKernel orderSearchKernel;
    private final PayOrderMapper payOrderMapper;

    /**
     * 查询一个月的未结算订单数量
     *
     * @param bo 参数对象
     */
    public R<?> getUnSettNum(SettlementMonthBo bo) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //管理员只能看自己数据
        if (LoginHelper.isAgent()) {
            bo.setAgentId(LoginHelper.getUserId());
        }
        //商户只能看自己数据
        if (LoginHelper.isClient()) {
            bo.setClientId(LoginHelper.getUserId());
        }

        // 构建查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        QueryBuilder payType = QueryBuilders.termQuery("payTypeId", bo.getPayTypeId());//收款平台
        QueryBuilder yearQuery = QueryBuilders.termQuery("createYear", bo.getYear());//年份
        QueryBuilder monthQuery = QueryBuilders.termQuery("createMonth", bo.getMonth());//月份
        QueryBuilder statusQuery = QueryBuilders.termQuery("status", 2);//支付成功
//        QueryBuilder refundQuery = QueryBuilders.termQuery("refundStatus", 2);//未退款
//        boolQuery.mustNot(refundQuery);
        boolQuery.filter(payType).filter(yearQuery).filter(monthQuery).filter(statusQuery);

        // 添加新的条件：refund_money < usd_money
        // 创建一个script查询，比较refund_money和usd_money字段
        Script script = new Script(ScriptType.INLINE, "painless", "doc['refundMoney'].value < doc['usdMoney'].value", Collections.emptyMap());
        ScriptQueryBuilder scriptQuery = QueryBuilders.scriptQuery(script);
        boolQuery.filter(scriptQuery);

        if (bo.getAgentId() != null) {
            boolQuery.filter(QueryBuilders.termQuery("agentId", bo.getAgentId()));
        }
        if (bo.getClientId() != null) {
            boolQuery.filter(QueryBuilders.termQuery("clientId", bo.getClientId()));
        }

        searchSourceBuilder.query(boolQuery);

        // 构建聚合查询，按照日期分组，并统计每天的已结算和未结算数量
        TermsAggregationBuilder dayAgg = AggregationBuilders.terms("day").field("createDay").size(31); // 假设最多31天
        dayAgg.subAggregation(AggregationBuilders.filter("sett_0", QueryBuilders.termQuery("sett", 0)) // 未结算
                .subAggregation(AggregationBuilders.count("sett_0_count").field("orderId")))
            .subAggregation(AggregationBuilders.filter("sett_1", QueryBuilders.termQuery("sett", 1)) // 已结算
                .subAggregation(AggregationBuilders.count("sett_1_count").field("orderId")));
        searchSourceBuilder.aggregation(dayAgg);

        // 执行查询，获取结果
        SearchResponse searchResponse = orderSearchKernel.highLevelSearchOrder(searchSourceBuilder);
        if(searchResponse == null) {
            return R.fail();
        }

        Aggregations aggregations = searchResponse.getAggregations();
        Terms dayTerms = aggregations.get("day");

        // 将每天的已结算和未结算数量存入Map中
        Map<String, Map<String, Long>> orderCountsByDay = new LinkedHashMap<>();
        for (Terms.Bucket bucket : dayTerms.getBuckets()) {
            String day = bucket.getKeyAsString();
            Map<String, Long> counts = new LinkedHashMap<>();
            long settledCount = ((ParsedFilter) bucket.getAggregations().get("sett_1")).getDocCount();
            long unsettledCount = ((ParsedFilter) bucket.getAggregations().get("sett_0")).getDocCount();
            counts.put("settled", settledCount);
            counts.put("unsettled", unsettledCount);
            orderCountsByDay.put(day, counts);
        }

        return R.ok(orderCountsByDay);
    }

    /**
     * 查询未结算订单列表
     *
     * @param bo 参数对象
     */
    public PageImpl<PayOrderVo> getUnSettList(SettlementListBo bo) {
        return orderSearchKernel.queryOrderEsPage(buildSettQuery(bo), bo.getPageNum() - 1, bo.getPageSize(), PayOrderVo.class, null, Arrays.stream(PayOrderVo.class.getDeclaredFields()).map(Field::getName).toArray(String[]::new));
    }

    /**
     * 查询未结算订单金额、数量
     *
     * @param bo 参数对象
     */
    public Map<String, Object> getUnSettToTal(SettlementListBo bo) {

        bo.setSett(null);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(buildSettQuery(bo));

        // 构建聚合查询，统计数量和usdMoney的和
        TermsAggregationBuilder dayAgg = AggregationBuilders.terms("sett").field("sett").size(2); //只有两种状态
        dayAgg.subAggregation(AggregationBuilders.sum("total_usd_money").field("usdMoney"));
        dayAgg.subAggregation(AggregationBuilders.sum("total_refund_money").field("refundMoney"));

        searchSourceBuilder.aggregation(dayAgg);
        searchSourceBuilder.size(0); // 设置返回结果数量为0，只获取聚合结果

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("num_0", 0);
        result.put("money_0", 0);
        result.put("num_1", 0);
        result.put("money_1", 0);

        // 执行查询，获取结果
        SearchResponse searchResponse = orderSearchKernel.highLevelSearchOrder(searchSourceBuilder);
        if(searchResponse == null) {
            return result;
        }

        Aggregations aggregations = searchResponse.getAggregations();
        Terms dayTerms = aggregations.get("sett");

        for (Terms.Bucket bucket : dayTerms.getBuckets()) {
            String sett = bucket.getKeyAsString();
            Aggregations agg = bucket.getAggregations();
            Sum sumAgg = agg.get("total_usd_money");
            Sum sumAgg2 = agg.get("total_refund_money");

            BigDecimal usdMoney = BigDecimal.valueOf(sumAgg.getValue()).setScale(2, RoundingMode.HALF_UP);
            BigDecimal refundMoney = BigDecimal.valueOf(sumAgg2.getValue()).setScale(2, RoundingMode.HALF_UP);

            result.put("num_" + sett, bucket.getDocCount()); // 获取hits中的total值);
            result.put("money_" + sett, usdMoney.subtract(refundMoney));
        }

        return result;
    }

    /**
     * 未结算的全部结算
     *
     * @param bo 查询参数
     */
    public R<?> setAllSett(SettlementAllBo bo) {

        SettlementListBo params = BeanUtils.copy(bo, SettlementListBo.class);

        //只查询未结算的
        params.setSett(0);

        // 执行查询
        PageImpl<PayOrderEs> page = orderSearchKernel.queryOrderEsPage(buildSettQuery(params), null, null, PayOrderEs.class, null, "orderId");

        List<Long> orderIdList = page.getContent().stream().map(PayOrderEs::getOrderId).collect(Collectors.toList());

        //修改结算状态
        updateSett(orderIdList, 1);

        return R.ok();
    }

    /**
     * 设为未结算
     *
     * @param bo 查询参数
     */
    public R<?> setUnSett(SettlementUnBo bo) {

        if (bo.getOrderIds() != null && !bo.getOrderIds().isEmpty()) {
            //设为未结算
            updateSett(bo.getOrderIds(), 0);
        }

        return R.ok();
    }

    /**
     * 修改结算状态
     *
     * @param orderIdList 主键集合
     * @param sett        结算状态
    结果
     */
    private void updateSett(List<Long> orderIdList, Integer sett) {
        List<List<Long>> lists = Lists.partition(orderIdList, 1000);

        lists.forEach(orderIds -> {

            PayOrder up = new PayOrder();
            up.setSett(sett);
            payOrderMapper.update(up, new QueryWrapper<PayOrder>().lambda().in(PayOrder::getOrderId, orderIds));

            List<UpdateQuery> queries = new ArrayList<>();
            for (Long orderId : orderIds) {
                Document document = Document.create();
                document.put("sett", sett);
                UpdateQuery updateQuery = UpdateQuery.builder(orderId.toString()).withDocument(document).build();
                queries.add(updateQuery);
            }

            // 批量更新
            orderSearchKernel.bulkUpdate(queries);
        });
    }

    /**
     * 构建查询未结算参数
     *
     * @param bo 参数对象
     */
    private BoolQueryBuilder buildSettQuery(SettlementListBo bo) {

        //管理员只能看自己数据
        if (LoginHelper.isAgent()) {
            bo.setAgentId(LoginHelper.getUserId());
        }
        //商户只能看自己数据
        if (LoginHelper.isClient()) {
            bo.setClientId(LoginHelper.getUserId());
        }

        // 构建查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        QueryBuilder payType = QueryBuilders.termQuery("payTypeId", bo.getPayTypeId());//收款平台
        QueryBuilder dateQuery = QueryBuilders.termsQuery("createDate", bo.getDateList());//年-月-日
        QueryBuilder statusQuery = QueryBuilders.termQuery("status", 2);//支付成功
//        QueryBuilder refundQuery = QueryBuilders.termQuery("refundStatus", 2);//未退款
//        boolQuery.mustNot(refundQuery);
        boolQuery.filter(payType).filter(dateQuery).filter(statusQuery);

        // 添加新的条件：refund_money < usd_money
        // 创建一个script查询，比较refund_money和usd_money字段
        Script script = new Script(ScriptType.INLINE, "painless", "doc['refundMoney'].value < doc['usdMoney'].value", Collections.emptyMap());
        ScriptQueryBuilder scriptQuery = QueryBuilders.scriptQuery(script);
        boolQuery.filter(scriptQuery);

        //结算类型（0:未结算 1:已结算）
        if (bo.getSett() != null) {
            boolQuery.filter(QueryBuilders.termQuery("sett", bo.getSett()));
        }

        if (bo.getAgentId() != null) {
            boolQuery.filter(QueryBuilders.termQuery("agentId", bo.getAgentId()));
        }

        if (bo.getClientId() != null) {
            boolQuery.filter(QueryBuilders.termQuery("clientId", bo.getClientId()));
        }

        return boolQuery;
    }
}
